#ifndef _HTTP_H_
#define _HTTP_H_

#define _GNU_SOURCE
#include <math.h>
/* ulibc: __USE_GUN
 * glibs: _GNU_SOURCE
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/time.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/crypto.h>

#include "list.h"
#include "safe_free.h"
#include "err_quit.h"

#define HTTP   0
#define HTTPS  1

#define REQUEST           0
#define IS_REQUEST        REQUEST
#define RESPONSE          1
#define IS_RESPONSE       RESPONSE

#define DEFAULT_HTTP_PORT   80
#define DEFAULT_HTTPS_PORT  443
#define HTTP_PROXY_PORT     8082
#define HTTPS_PROXY_PORT    8083
#define LEN_SSL_RECORD      16384

/*
 * 关于PR的说明
 * 0-PR_NONE:           i
 *         既没有Content-Type也没有content_length, 也没有chunked                   
 *         携带Content-type但是不携带Content-length或者chunk的请求(keep-alive或者close)
 *         携带Content-type但是不携带Content-length或者chunk的响应(keep-alive)
 * 1-PR_TXT_LEN:        body_type ∈　text_table[],  content-length
 * 2-PR_TXT_CHUNK:      body_type ∈　text_table[],  chunked
 * 5-PR_TXT_NONE:       body_type ∈　text_table[], !content-length && !chunked
 *      (connection-close响应) 
 * 3-PR_NONE_TXT_LEN:   body_type !∈　text_table[], content-length
 * 4-PR_NONE_TXT_CHUNK: body_type !∈　text_table[], chunked
 * 6-PR_NONE_TXT_NONE:  body_type !∈　text_table[], !content-length && !chunked
 *      (connection-close响应) 
 */
#define PR_NONE          0
#define PR_TXT_CHUNK     1
#define PR_TXT_LEN       2
#define PR_NONE_TXT_LEN  3
#define PR_NONE_TXT_CHK  4
#define PR_TXT_NONE      5
#define PR_NONE_TXT_NONE 6
#define ERR_CHK_MEM      -10
#define ERR_BODY_MEM      -10


#define ENCD_NONE      0
#define ENCD_GZIP      1
//not used
#define ENCD_BR
#define ENCD_DEFLATE
#define ENCD_COMPRESS



#define ENCD_KEEP      0
#define ENCD2FLATE     1

#define LEN_METHOD    64 
#define LEN_URL       512
#define LEN_VER       32
#define LEN_STAT_CODE 32
#define LEN_STAT_INFO 128

#define LEN_HOST         512
#define LEN_FIELD_KEY    128
#define LEN_FIELD_VALUE  960
#define LEN_LINE         2048

#define LEN_HEADER       8192
#define LEN_CHUNK        8192
#define LEN_BUF          1024
#define CHUNK 8192

/* 文本文件大小限制8MB */
#define TXT_LEN_LIMIT    (1024 * 1024 * 8)


/* seconds */
#define TIMEOUT_CONNECT  5
#define CLIENT_TIMEOUT   2

/* 
 * keepalive 超时15分钟
 * 加入超时会引入新的问题，如果建立了长连接下载大文件，网速又较慢
 * 此时的情况是: 客户端一直处于接受状态，如果持续时间超过15分钟，c2s会发生超时，长连接会被断掉
 */
#define HTTP_TIMEOUT_SEC 900
#define HTTP_TIMEOUT_MS  0
/* CLOSE_WAIT问题出现的频率很低 */
#define SESSION_TIME_OUT    60
#define TIME_FIX_CLOSE_WAIT 300 

typedef struct _http_header{
	char   method[LEN_METHOD];
	char   url[LEN_URL];
	char   ver[LEN_VER];
	char   stat_code[LEN_STAT_CODE];
	char   stat_info[LEN_STAT_INFO];
    char   crlf[3];
    struct list_head head;
}http_header_t;

typedef struct _http_field
{
	char   key[LEN_FIELD_KEY];
	char   value[LEN_FIELD_VALUE];
	struct list_head list;
}http_field_t;


typedef struct content_type_text
{
    char *type;
}c_type_t;

typedef struct {
    char type[128];
    struct list_head list;
}c_type_ext_t;

typedef struct {
    int  chk_size;
    char *chk_ext;
    char chk_crlf[3];
    /* 非trailer */
    char *body;
    /* trailer */
    int  trl_size;
    char *trailer;
    char body_crlf[3];
    struct list_head list;
}http_chunk_t;


/* 定义在http.c中 */
extern int proxy;
extern c_type_t text_table[];

extern int readn(int fd, SSL *ssl, void *buff, int n);
extern int read_line(int fd, SSL *ssl, char *buff, int cnt);
extern int read_http_header(int fd, SSL *ssl, char *buff, int cnt);
extern int my_write(int fd, SSL *ssl, const char *fmt, ...);

extern int parse_http_header(const char *buf, http_header_t *header);
extern int parse_http_field(const char *line, http_field_t *field);
extern void free_http_header(http_header_t *header);
extern int hex2dec(char *hex, unsigned int *dec);
extern int erase_nhex(char *chunk_size);
extern long long int get_pr_encd(struct list_head *head, int *pr, int req_or_rsp);
extern int get_host_port(http_header_t *header, char *host, unsigned short *port);
extern int get_host_port_in_url(const char *_url, char *host, unsigned short *port);
extern int is_http_req_rsp(http_header_t *header);
extern int http_header_tostr(http_header_t *header, char *buff);
extern int rewrite_clen_encd(struct list_head *head, int content_length, int gunzip);
extern int rewrite_encd(struct list_head *head, int encd);
int read_all_chunk(int fd, SSL *ssl, struct list_head *head);
int read_parse_chunk(int fd, SSL* ssl, http_chunk_t *chunk);
int read_parse_chk_size_ext_crlf(int fd, SSL* ssl, http_chunk_t *chunk);
int read_parse_chk_body_crlf(int fd, SSL *ssl, http_chunk_t *chunk);
int is_empty_line(const char *line);
int http_chunk_to_buff(http_chunk_t *chunk, unsigned char **buf, unsigned int *len);
int http_all_chunk_to_buff(struct list_head *head, unsigned char **buff, unsigned int *len);
void free_http_chunk(http_chunk_t *chunk);
void free_chunk_list(struct list_head *head); 
int get_peer_ip(int fd, char *ip, unsigned short *port);

int print_ssl_error(SSL *ssl, int ret, const char *remark);

#endif

